package cn.spring.framework.redis.core.redis;

import cn.spring.framework.redis.codec.CodecEnum;
import cn.spring.framework.redis.codec.data.MapData;
import cn.spring.framework.redis.config.LzmhRedisProperties;
import cn.spring.framework.redis.constant.Constants;
import cn.spring.framework.redis.core.FillCache;
import cn.spring.framework.redis.core.MapOperator;
import cn.spring.framework.redis.core.Refresher;
import cn.spring.framework.redis.excutor.AsyncCacheExecutor;
import cn.spring.framework.redis.excutor.CacheExecutor;
import cn.spring.framework.redis.excutor.FutureAdapter;
import cn.spring.framework.redis.excutor.SyncCacheExecutor;
import cn.spring.framework.redis.service.RedisService;
import org.springframework.util.CollectionUtils;

import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author xingkong
 * @Title: RedisMapOperator
 * @Description: map类型操作实现
 * @date 2021/11/14 17:41
 */
public class RedisMapOperator extends AbstractRedisOperator implements MapOperator {

    /**
     * 异步任务执行器
     */
    protected CacheExecutor<Map<String, Object>> asyncCacheExecutor = new AsyncCacheExecutor<>();

    public RedisMapOperator(LzmhRedisProperties lzmhRedisProperties, RedisService redisService) {
        super(lzmhRedisProperties, redisService);
    }


    @Override
    public Map<String, Object> hGetAll(String key, long expire, Refresher<Map<String, Object>> refresher) {
        return this.hGetAll(key, expire, refresher, new SyncCacheExecutor<>()).getData();
    }

    @Override
    public Future<Map<String, Object>> hGetAllAsync(String key, long expire, Refresher<Map<String, Object>> refresher) {
        return this.hGetAll(key, expire, refresher, this.asyncCacheExecutor);
    }

    @Override
    public Future<Map<String, Object>> hGetAllAsync(String key, long expire, Refresher<Map<String, Object>> refresher, ExecutorService executorService) {
        return this.hGetAll(key, expire, refresher, new AsyncCacheExecutor<>(executorService));
    }

    @Override
    public Object hGet(String key, String field, long expire, Refresher<Map<String, Object>> refresher) {
        //缓存数据已经过期
        Map<String, Object> data = this.hGetAll(key, expire, refresher);
        if (data == null) {
            return null;
        }
        return data.get(field);
    }

    @Override
    public Future<Map<String, Object>> hGetAsync(String key, String field, long expire, Refresher<Map<String, Object>> fluster) {
        Map<String, Object> fieldMap = redisService.getCacheMapValue(key, field);
        if (CollectionUtils.isEmpty(fieldMap)) {
            return this.hGetAllAsync(key, expire, fluster);
        }
        return FutureAdapter.runAndGetFuture(fieldMap);
    }

    @Override
    public Future<Map<String, Object>> hGetAsync(String key, String field, long expire, Refresher<Map<String, Object>> fluster, ExecutorService executorService) {
        Map<String, Object> fieldMap = redisService.getCacheMapValue(key, field);
        if (CollectionUtils.isEmpty(fieldMap)) {
            return this.hGetAllAsync(key, expire, fluster, executorService);
        }
        return FutureAdapter.runAndGetFuture(fieldMap);
    }

    private FutureAdapter<Map<String, Object>> hGetAll(String key, long expire, Refresher<Map<String, Object>> refresher,
                                                       CacheExecutor<Map<String, Object>> cacheExecutor) {
        Map<String, Object> resMap = redisService.getCacheMap(key);
        //数据解码
        MapData mapData = (MapData) this.getDecodeData(resMap, CodecEnum.MAP);
        boolean invalid = mapData.isInvalid();

        if (invalid) {
            return (FutureAdapter<Map<String, Object>>) cacheExecutor.executor(() -> this.fillCache(new FillCache<Map<String, Object>>() {
                @Override
                public String getKey() {
                    return key;
                }

                @Override
                public Map<String, Object> getIgnoreValidData() {
                    return (Map<String, Object>) blockIfNeed(key);
                }

                @Override
                public Map<String, Object> getCacheData() {
                    //执行具体的获取缓存数据逻辑
                    Map<String, Object> data = refresher.refresh();
                    //对过期时间进行延长
                    long newExpire = expire;
                    if (expire != Constants.REDIS_TTL_FOREVER) {
                        newExpire = getExtendExpire(expire);
                    }
                    //对数据进行编码操作
                    MapData encodeMap;
                    if (CollectionUtils.isEmpty(data)) {
                        encodeMap = (MapData) getEncodeData(lzmhRedisProperties.getEmptyObjTtL(), data, CodecEnum.MAP);
                    } else {
                        encodeMap = (MapData) getEncodeData(expire * 1000, data, CodecEnum.MAP);
                    }
                    //填充缓存
                    redisService.setCacheMap(key, encodeMap.getData(), newExpire, TimeUnit.SECONDS);
                    return data;
                }
            }));
        } else {
            //缓存中存在数据且未过期
            Map<String, Object> resultMap = mapData.getData();
            //处理库中不存在，但是缓存了空对象的处理
            if (resultMap.size() == 0) {
                return FutureAdapter.runAndGetFuture(null);
            }
            return FutureAdapter.runAndGetFuture(mapData.getData());
        }

    }

    @Override
    protected Object getDataIgnoreValid(String key) {
        //检查是否已经有其他线程刷新完缓存
        Map<String, String> cacheMap = redisService.getCacheMap(key);
        return cacheMap;
    }
}
